import svg

class Paddle:
    """
        Handles paddle rendering and updates
    """
    def __init__(self, p, x, y, w, h):
        self.p = p
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.element = document.createElementNS(self.p.vg.ns, "rect")
        self.element.setAttribute("fill", "#ffffff")
        self.element.setAttribute("stroke", "#000000")
        self.element.setAttribute("x", self.x)
        self.element.setAttribute("y", self.y)
        self.element.setAttribute("rx", 4)
        self.element.setAttribute("width", self.w)
        self.element.setAttribute("height", self.h)
        self.p.vg.g.appendChild(self.element)

    def update(self):
        self.element.setAttribute("x", self.x)

    def addX(self, value):
        self.x += value
        self.update()

class Ball:
    """
        Handles motion, collision, and rendering of ball
    """
    def __init__(self, p, x, y, r):
        self.p = p
        self.x = x
        self.y = y
        self.r = r
        self.vx = 1
        self.vy = 1
        self.speed = 1
        self.element = document.createElementNS(self.p.vg.ns, "circle")
        self.element.setAttribute("fill", "#ffffff")
        self.element.setAttribute("stroke", "#000000")
        self.element.setAttribute("cx", self.x)
        self.element.setAttribute("cy", self.y)
        self.element.setAttribute("r", self.r)
        self.p.vg.g.appendChild(self.element)

    def update(self):
        s2 = (self.speed / 20) + 4
        self.x += self.vx * s2
        self.y += self.vy * s2 * 1.25
        if self.x < self.r:
            self.x = self.r
            self.vx = 1
            self.p.fxs.append(Boom(self.p, self.x-self.r, self.y))
        elif self.x > self.p.vg.width - self.r:
            self.x = self.p.vg.width - self.r
            self.vx = -1
            self.p.fxs.append(Boom(self.p, self.x+self.r, self.y))
        if self.y < self.r:
            self.y = self.r
            self.vy = 1
            self.p.fxs.append(Boom(self.p, self.x, self.y-self.r))
        self.element.setAttribute("cx", self.x)
        self.element.setAttribute("cy", self.y)

    def checkPaddle(self):
        hit = 0
        if self.y > self.p.pad.y - self.r:
            if (self.x > self.p.pad.x - self.r \
                    and self.x < self.p.pad.x + self.p.pad.w + self.r):
                self.vy = -1
                self.y = self.p.pad.y - self.r
                hit = 1
                self.p.fxs.append(Boom(self.p, self.x, self.y+self.r))
            elif self.y > self.p.vg.height - self.r:
                self.y = self.p.vg.height - self.r
                self.vy =- 1
                hit = 2
                self.p.fxs.append(Boom(self.p, self.x, self.y+self.r))
        return hit

class Score:
    """
        Keep track of, and display, score
        (also updates background color and game speed as score changes)
    """
    def __init__(self, p):
        self.p = p
        self.value = 0
        self.element = document.createTextNode(str(self.value))
        self.colors = [
            (0.0, 0.0, 1.0),
            (0.0, 1.0, 1.0),
            (0.0, 1.0, 0.0),
            (1.0, 1.0, 0.0),
            (1.0, 0.0, 0.0),
            (1.0, 0.0, 1.0),
            (0.0, 0.0, 0.0),
            (1.0, 1.0, 1.0),
        ]
        document.body.appendChild(document.createElement("br"))
        document.body.appendChild(document.createTextNode("Score: "))
        document.body.appendChild(self.element)
        self.update()

    def updateRGB(self):
        real = float(self.value) / 10.0
        int1 = int(real)
        i1 = float(real - int1)
        i2 = 1.0 - i1
        int1 = int1 % len(self.colors)
        int2 = (int1 + 1) % len(self.colors)
        r = self.colors[int1][0] * i2 + self.colors[int2][0] * i1
        g = self.colors[int1][1] * i2 + self.colors[int2][1] * i1
        b = self.colors[int1][2] * i2 + self.colors[int2][2] * i1
        self.p.vg.bgColor(int(255.0*r), int(255.0*g), int(255.0*b))

    def update(self):
        if self.value < 0:
            self.value = 0
        self.element.nodeValue = str(self.value * 100)
        self.p.bal.speed = float(self.value)
        self.updateRGB()

    def addValue(self, value):
        self.value += value
        self.update()

class Boom:
    """
        Simple radiating circle for explosion effect
    """
    def __init__(self, p, x, y):
        self.p = p
        self.x = x
        self.y = y
        self.r = 0.0
        self.remove = 0
        self.element = document.createElementNS(self.p.vg.ns, "circle")
        r = lambda: Math.random()
        self.element.setAttribute("fill", svg.rgbf(r(), r(), r()))
        self.element.setAttribute("cx", self.x)
        self.element.setAttribute("cy", self.y)
        self.element.setAttribute("r", self.r)
        self.p.vg.g.appendChild(self.element)

    def update(self):
        self.r += 2.0
        if self.r > 50:
            self.remove = 1
            self.p.vg.g.removeChild(self.element)
        else:
            self.element.setAttribute("r", self.r)
            self.element.setAttribute("opacity", 1.0 - (self.r / 50.0))

class Game:
    """
        Game object, handles game construction / main step
    """
    def __init__(self):
        self.vg = svg.Svg(document.body, 320, 240)
        self.pad = Paddle(self, 10, 240-9, 70, 8)
        self.bal = Ball(self, 10, 10, 5)
        self.sco = Score(self)
        self.fxs = []
        self.keys = []
        document.onkeydown = lambda e: self.keys.__setitem__(e.keyCode, 1)
        document.onkeyup = lambda e: self.keys.__setitem__(e.keyCode, 0)

    def clean(self):
        """
            Cleanup effects (remove old explosions)
        """
        cleanup = []
        for i in xrange(len(self.fxs)):
            self.fxs[i].update()
            if self.fxs[i].remove:
                cleanup[:0] = [i]
        for i in cleanup:
            del self.fxs[i]

    def step(self):
        """
            One game step
        """
        if self.keys[39]:
            self.pad.addX(10)
        elif self.keys[37]:
            self.pad.addX(-10)
        hit = self.bal.checkPaddle()
        if hit == 1:
            self.sco.addValue(1)
        elif hit == 2:
            self.sco.addValue(-5)
        self.bal.update()
        self.clean()

game = Game()
setInterval(lambda: game.step(), 40)

